import { RegexHelper } from './Helpers/RegexHelper';
import { ResponseParser } from './Helpers/ResponseParser';
import { Message } from './Message';
import { Response } from './Response';
import { Store } from './Store';

export class Folder {
  private store: Store;
  private name: string;
  private openStatus: boolean = false;

  public constructor(store: Store, name: string) {
    this.store = store;
    this.name = name;
  }

  private toJSON() {
    return {
      name: this.name,
    };
  }

  // 获取文件夹名称
  public getName() {
    return this.name;
  }

  // 获取文件夹内邮件数量(全部/未读/最近)
  public getMessageCount() {
    return new Promise<{
      messages: number;
      unseen: number;
      recent: number;
    }>(async (resolve, reject) => {
      if (!this.openStatus) {
        await this.open();
      }
      const response = await this.store.getClient().sendCommand('STATUS INBOX (MESSAGES UNSEEN RECENT)');
      if (!response.ok) {
        response.msg = 'getMessageCount error';
        reject(response);
      } else {
        const { payload } = response;
        const matchRes = RegexHelper.matchAll(payload, /(MESSAGES|UNSEEN|RECENT)\s([0-9]*)/gi);
        const countMap = {
          messages: 0,
          unseen: 0,
          recent: 0,
        };
        matchRes.forEach((match) => {
          const type = match[1].toLowerCase() as keyof typeof countMap;
          const count = Number(match[2].toLowerCase());
          if (Object.keys(countMap).includes(type)) {
            countMap[type] = count;
          }
        });
        resolve(countMap);
      }
    });
  }

  // 关闭文件夹
  public close() {
    this.openStatus = false;
  }

  // 打开文件夹,返回总邮件数
  public open() {
    return new Promise<number>(async (resolve, reject) => {
      const client = this.store.getClient();
      const response = await client.sendCommand(`SELECT ${this.name}`);
      // 失败
      if (!response.ok) {
        response.msg = 'folder:open error';
        reject(response);
      } else {
        // 成功
        const { payload } = response;
        const matchCount = payload.matchAll(/\*\s([0-9]*)\sEXISTS/gi);
        const messageCountRaw = matchCount.next().value?.[1] ?? 0;
        const messageCount = Number(messageCountRaw);
        // 关闭其他的Folder
        this.store['folders'].forEach((folder) => {
          folder !== this && folder.close();
        });
        // 自身标记为打开状态
        this.openStatus = true;
        resolve(messageCount);
      }
    });
  }

  // 当前文件夹的打开状态
  public isOpen() {
    return this.openStatus;
  }

  // 获取 Message 对象,无范围则返回所有
  public getMessages(range?: [number, number]) {
    return new Promise<Message[]>(async (resolve, reject) => {
      const client = this.store.getClient();
      // 修复之前代码问题，某些邮件服务器商会返回所有的邮件，这里直接获取所有的然后按range进行截取
      // 1. 获取所有 UID
      const response = await client.sendCommand(`UID SEARCH ALL`);
      if (!response.ok) {
        response.msg = 'folder:getMessage error';
        reject(response.error);
      } else {
        const regex = /^\*\sSEARCH\s(.*)[\r\n]/gim;
        const matches = [...response.payload.matchAll(regex)];
        let allUids: string[] = [];
        matches.forEach((match) => {
          allUids.push(...match[1].split(' ').filter(Boolean));
        });
        // 2. 按 range 截取
        if (range) {
          const [start, end] = range;
          allUids = allUids.slice(start - 1, end); // 注意数组下标从0开始
        }
        const messages: Message[] = allUids.map(uid => new Message(this, uid));
        resolve(messages);
      }
    });
  }

  // uid获取指定的message
  public getMessageByUID(uid: string) {
    return new Message(this, uid);
  }

  // index获取指定的message
  public getMessageByIndex(index: number) {
    return new Promise<Message>(async (resolve, reject) => {
      const messages = await this.getMessages([index, index]);
      if (messages.length) {
        resolve(messages[0]);
      } else {
        reject(Response(207, 'folder: getMessageByIndex error'));
      }
    });
  }

  // 批量更新 messages headers
  public updateHeaders(messages: Message[]) {
    return new Promise<void>(async (resolve, reject) => {
      const client = this.store.getClient();
      const messageMap: Record<string, Message> = {};
      const uidStr = messages
        .map((msg) => {
          messageMap[msg.uid] = msg;
          return msg.uid;
        })
        .join(',');

      const response = await client.sendCommand(
        `UID FETCH ${uidStr} (RFC822.SIZE BODY.PEEK[HEADER.FIELDS (Subject From Date Cc Bcc To Content-Type)])`
      );
      // 失败
      if (!response.ok) {
        response.msg = 'Folder: updateHeaders error';
        reject(response.error);
      } else {
        // 成功
        const headersArr = ResponseParser.parseFetchHeader(response.payload);
        headersArr.forEach((headers) => {
          const uid = headers.uid;
          if (uid) {
            const msg = messageMap[uid];
            msg['headers'] = headers;
          }
        });
        resolve();
      }
    });
  }
}
